
import time
import random 
#from operator import itemgetter

#from dexen.user.libs import constants as dc
#from dexen.user.libs.algorithms import ea

import dexen

import myind
from libs import constants, input_reading



class FeedbackTask(dexen.Task):
    def __initialize__(self):
        self.boxes = input_reading.read_input()
        self.nboxes = len(self.boxes)
        self.objectives = [constants.WIDTH, constants.HEIGHT]
        self.focus = 0
    
    def __isvalid__(self, ind):
        return ind.area != None
    
    def __execute__(self, inds):
        print "I am in feedback2"      
        time.sleep(10)
        #env_selector = ea.EnvironmentalSelection()
        #env_selector.setObjectives(("width", dc.MINIMIZE), ("height", dc.MINIMIZE))
        #env_selector.setSelectionRate(0.5)
        #deads, alives = env_selector.select(inds)
        deads = inds[:200]
        alives = inds[200:]
        for dead_ind in deads:
            dead_ind.kill()
        for alive_ind in alives:
            alive_ind.revive()
        parents = alives
        new_gen = self.reproduction(parents)
        self.focus = 1 - self.focus
        
        assert len(deads) == 200
        assert len(new_gen) == 200  
        return inds + new_gen 
    
    def generate_encoding(self, genotype):
        for i, box_no in enumerate(genotype["gamma_x"]):
            genotype["encoding"][box_no] = dict(x=i)
        for i, box_no in enumerate(genotype["gamma_y"]):
            genotype["encoding"][box_no]['y'] = i    
    
    def mutate(self, genotype):
        for _ in range((self.nboxes+19)/20):
            box_no = random.randint(0, self.nboxes - 1)
            genotype['orient'][box_no] = 1 - genotype['orient'][box_no]
    
    def generate_child(self, parent1, parent2):
        child = myind.MyIndividual()
        child_gen = {
                "gamma_x" : range(self.nboxes),
                "gamma_y" : range(self.nboxes),
                "encoding" : [None] * self.nboxes,
                "orient" : [None] * self.nboxes
            }
        child.genotype = child_gen
        
        gen1 = parent1.genotype
        gen2 = parent2.genotype    
        
        for i in range(self.nboxes):
            child_gen['gamma_x'][i] = gen1['gamma_x'][i]
            child_gen['gamma_y'][i] = gen1['gamma_y'][i]
            child_gen['orient'][i] = gen1['orient'][i]
        
        left_x = random.randint(0, self.nboxes - 2)
        right_x = random.randint(left_x + 1, self.nboxes - 1)
        
        neighbor_ids = []
        for i in range(left_x, right_x + 1):
            neighbor_ids.append(gen1['gamma_x'][i])
        
        rows = [] 
        """ This keeps the y coordinates of the ones in the window """
        for i in neighbor_ids:
            rows.append(gen1['encoding'][i]['y'])
        rows.sort()
        
        neighbor_ids.sort(cmp=lambda a,b: gen2['encoding'][a]['x'] - gen2['encoding'][b]['x'])
        cnt = 0
        for i in range(left_x, right_x + 1):
            child_gen['gamma_x'][i] = neighbor_ids[cnt]
            cnt = cnt + 1
        
        neighbor_ids.sort(cmp=lambda a,b: gen2['encoding'][a]['y'] - gen2['encoding'][b]['y'])
        cnt = 0
        for i in rows:
            child_gen['gamma_y'][i] = neighbor_ids[cnt]
            cnt = cnt + 1
        
        self.generate_encoding(child_gen)
        self.mutate(child_gen)
    
        return child
            
    def generate_children(self, parent1, parent2):    
        child1 = self.generate_child(parent1, parent2)
        child2 = self.generate_child(parent2, parent1)
        return [child1, child2]
    
    def perturb_parents(self, individuals):
        shuffle_size = len(individuals) / 10
        pos = random.randint(0, len(individuals) - shuffle_size)
        shuffled_sublist = individuals[pos:pos + shuffle_size]
        random.shuffle(shuffled_sublist)
        individuals[pos:pos + shuffle_size] = shuffled_sublist
        return 
    
    
    def reproduction(self, inds):
        inds.sort(key = lambda ind : getattr(ind, self.objectives[self.focus]))
        self.perturb_parents(inds)
    
        """ Check the population size if it is even """
        new_gen = []    
        for i in range(0, len(inds) - 1, 2):
            new_gen.extend(self.generate_children(inds[i], inds[i+1]))
        
        if len(inds) % 2 != 0:
            new_gen.append(self.generate_child(inds[-2], inds[-1]))
        
        return new_gen
     
